package com.wjk.kylin.mall.gateway.filter;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.nimbusds.jose.JWSObject;
import com.wjk.kylin.mall.common.core.constant.Constants;
import com.wjk.kylin.mall.common.core.exception.BaseError;
import com.wjk.kylin.mall.common.core.exception.BaseException;
import com.wjk.kylin.mall.common.core.util.StringUtils;
import com.wjk.kylin.mall.common.redis.utils.RedisUtil;
import com.wjk.kylin.mall.gateway.config.IgnoreUrlsConfig;
import com.wjk.kylin.mall.gateway.feign.FeignHolder;
import com.wjk.kylin.mall.gateway.util.SysContext;
import com.wjk.kylin.mall.gateway.util.WebUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 全局过滤器
 */
@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    //    @Autowired
//    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;
    @Autowired
    private FeignHolder feignHolder;

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    @SneakyThrows
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //白名单 登出接口 不会走AuthorizationManager
        String traceId = request.getHeaders().getFirst(Constants.TRACE_ID_KEY);
        if (StringUtils.isBlank(traceId)) {
            traceId = IdUtil.simpleUUID();
            log.info("header traceId is null create new traceId:{}", traceId);
        }
        MDC.put(Constants.TRACE_ID_KEY, traceId);
        SysContext.setTraceId(traceId);

        String path = request.getURI().getPath();
        log.info("令牌鉴定过滤器-访问路径:[{}]", path);

        //1.白名单
        if (StringUtils.matches(path, ignoreUrlsConfig.getUrls())) {
            //白名单请求后续也要加traceId 不会走AuthorizationManager
            exchange.getRequest().mutate().header(Constants.TRACE_ID_KEY, traceId).build();
            return chain.filter(exchange);
        }

        //2.token为空 或不是Bearer 开始
        String token = request.getHeaders().getFirst(Constants.TOKEN_HEADER);
        if (StringUtils.isBlank(token) || !token.startsWith(Constants.TOKEN_TYPE_BEARER)) {
//            return chain.filter(exchange);
            return WebUtils.writeFailedToResponse(exchange.getResponse(), BaseError.TOKEN_INVALID_IS_BLANK);
        }

        //3.截取token
        token = token.replace(Constants.TOKEN_TYPE_BEARER, Strings.EMPTY);
        if (StringUtils.isBlank(token)) {
            return WebUtils.writeFailedToResponse(exchange.getResponse(), BaseError.TOKEN_INVALID_IS_BLANK);
        }

        //4.解析token,判断是否存在
        JWSObject jwsObject = JWSObject.parse(token);
        String payload = jwsObject.getPayload().toString();
        if (StringUtils.isBlank(payload)) {
            return WebUtils.writeFailedToResponse(exchange.getResponse(), BaseError.TOKEN_INVALID_OR_EXPIRED);
        }

        //payload：[{"user_id":1399740196977999873,"user_name":"wjk","scope":["all"],"nick_name":"蔡家坡","exp":1622900677,
        //"authorities":["ceo","admin","super_admin"],"jti":"a4ad1143-088d-4252-8864-891a301cf67d","client_id":"client"}]
        //4.解析JWT获取jti，以jti为key判断redis的黑名单列表是否存在，存在拦截响应token失效
        JSONObject jsonObject = JSONObject.parseObject(payload);
        String jti = jsonObject.getString(Constants.JWT_JTI);
        boolean isBlack = redisUtil.hasKey(Constants.TOKEN_BLACKLIST_PREFIX + jti);
        if (isBlack) {
            return WebUtils.writeFailedToResponse(response, BaseError.TOKEN_ACCESS_FORBIDDEN);
        }

        //5.JWT过期时间戳(单位:秒) 验证token是否过期
        Long exp = jsonObject.getLong(Constants.JWT_EXP);
        Long currentTimeSeconds = System.currentTimeMillis() / 1000;
        if (exp < currentTimeSeconds) {
            return WebUtils.writeFailedToResponse(exchange.getResponse(), BaseError.TOKEN_INVALID_OR_EXPIRED);
        }
        String userId = jsonObject.getString(Constants.USER_ID_KEY);
        String userName = jsonObject.getString(Constants.USER_NAME_KEY);
        String nickName = jsonObject.getString(Constants.NICK_NAME_KEY);
        if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(userName)) {
            return WebUtils.writeFailedToResponse(exchange.getResponse(), BaseError.TOKEN_INVALID_OR_EXPIRED);
        }

        /**
         * TODO 只能异步调用
         */
//        List<Long> roleIdList = new ArrayList<>();
//        Future<List<Long>> future = feignHolder.getRoleIdByComponent(path,request.getHeaders().getFirst(Constants.TRACE_ID_KEY));
//        try {
//            roleIdList = future.get();
//        } catch (Exception e) {
//            log.error("鉴权管理器-访问路径：[{}],调用Feign接口异常,e:", path, e);
//            throw new BaseException("鉴权异常，请重新尝试！");
//        }

        //6.request写入JWT的载体信息,以及用户id和账号
        request = exchange.getRequest().mutate()
                .header(Constants.JWT_PAYLOAD_KEY, payload)
                .header(Constants.USER_ID_KEY, userId)
                .header(Constants.USER_NAME_KEY, userName)
                .header(Constants.NICK_NAME_KEY, nickName)
                .header(Constants.TRACE_ID_KEY, traceId)
                .build();
        exchange = exchange.mutate().request(request).build();

        SysContext.removeTraceId();
        return chain.filter(exchange);
    }


}
